package com.progenies.ecg.asm31.model.factory;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import com.progenies.ecg.asm31.model.codeparts.IConfigurableCodePart;
import com.progenies.ecg.asm31.model.codeparts.IConfigurableConstructorObject;
import com.progenies.ecg.asm31.util.GenerationUtils;
import com.progenies.ecg.asm31.util.VariableRegister;
import com.progenies.ecg.generator.ClassGeneratorFactory;
import com.progenies.ecg.model.ClassObject;
import com.progenies.ecg.model.ConstructorObject;
import com.progenies.ecg.model.ParameterObject;
import com.progenies.ecg.model.codeparts.CodeBlock;
import com.progenies.ecg.model.codeparts.lines.LineFactory;

/**
 * Defines a class constructor, with the implementation of ASM31 bytecode generation.
 * @author ofc587a87
 *
 */
final class ConstructorObjectASM31 extends ConstructorObject implements IConfigurableConstructorObject
{
	private ClassVisitor visitor;
	private ClassObject parent;
	private VariableRegister varRegister;
	
	/**
	 * Creates a default constructor, public without parameters
	 */
	ConstructorObjectASM31() {
		this((ParameterObject<?>[])null);
	}
	
	/**
	 * Creates a constructor, public with parameters
	 */
	public ConstructorObjectASM31(ParameterObject<?> parameters[])
	{
		this.modifiers=new int[] {Opcodes.ACC_PUBLIC};
		this.parameters=parameters;
	}

	/* (non-Javadoc)
	 * @see com.progenies.ecg.asm31.model.factory.IConfigurableConstructorObject#generateBytecode()
	 */
	@Override
	public void generateBytecode()
	{
		if(this.visitor==null )
			throw new IllegalStateException("Constructor has not been configurated, please use ClassGenerator");
		
		MethodVisitor mv=visitor.visitMethod(GenerationUtils.getAccessTranslated(modifiers), getName(), GenerationUtils.translateDescription(this.parameters, null), null, GenerationUtils.getClassesNames(this.exceptions));
		mv.visitCode();
		
		//register parameters as local variables
		if(this.parameters!=null)
		{
			for(ParameterObject<?> param : parameters)
			{
				varRegister.registerLocalVariable(param.getName(), Type.getType(param.getClassType()));
			}
		}
		
		//if there is no code block, i'll invoke a default super()
		if(this.code==null)
		{
			LineFactory factory=ClassGeneratorFactory.getClassGenerator().lineFactory;
			CodeBlock block=factory.createCodeBlock();
			block.add(factory.methods.invokeSuperConstructor());
			block.add(factory.methods.returnEmpty());
			
			this.code=block;
		}
		
		//i'll manage if the user forgot to add empty return
		GenerationUtils.checkHasReturn(this);
		
		//configure code block
		((IConfigurableCodePart)this.code).configure(mv, this, varRegister);	
		
		//generate bytecode
		this.code.generateBytecode();
		
		//ends method
		mv.visitMaxs(Short.MAX_VALUE, Short.MAX_VALUE); //Auto calculated frames
		mv.visitEnd();

	}

	/* (non-Javadoc)
	 * @see com.progenies.ecg.asm31.model.factory.IConfigurableConstructorObject#configure(org.objectweb.asm.ClassVisitor, com.progenies.ecg.model.ClassObject, com.progenies.ecg.asm31.util.VariableRegister)
	 */
	@Override
	public void configure(ClassVisitor visitor,	ClassObject parent, VariableRegister varRegister) {
		this.visitor=visitor;
		this.parent=parent;
		this.varRegister=varRegister;
	}

	/* (non-Javadoc)
	 * @see com.progenies.ecg.asm31.model.factory.IConfigurableConstructorObject#getParent()
	 */
	@Override
	public ClassObject getParent() {
		return parent;
	}

}
